package Main;

import java.io.IOException;
import java.util.Random;
import java.util.Vector;
import javax.microedition.lcdui.*;
import javax.microedition.io.file.*;

public class Game_Graphics extends Canvas implements CommandListener
{
    final static int INIT_SCORE = 7;
    
    int time = 0;
    double w = 0.4;
    static int eog = 0;    //variable used to indicate enf of game
    int weaponsGen;  //controls speed at witch weapons are generated
    int explode = 0; //set to '1' if the ball hits the back of one of the players sides
    double expSize = 0.00; //size of explosion when ball hits the left and right sides of the screen
    int expInc = 0; //indicates whether explosion is increasing or decreasing
    int explY = 0; //the Y coordinate where the ball hit the side
    int explX = 0; //the side of the screen where the ball hit
    int doShoot = 7; //controls the AI shooting choice
    int reloadTime = 0; //stops Machine Gun maniacs
    Image bulletPlayer = null;
    Image bulletOpp = null;
    Image lose = null;
    Image win  = null;
    paddle player, opponent; public paddle GetPlayer() { return player; } public paddle GetOpponent() { return opponent; } 
    GameObjects ball;
    Button_Manager button_man;

    Vector objects;

    double accelerometerInput = 0;  
    double clientAccelerometerInput = 0;  
    
    Command cancelCommand;

    GameObjects getBall() { return ball; }
    
    void setBallPositionX(double newX) { ball.setX(newX); }
    void setBallPositionY(double newY) { ball.setY(newY); }
    void setBallVelocityY(double newY) { ball.setVelocityY(newY); }
            
    void setOpponentPosition(double opp) { opponent.setY(opp); }
    void setRightPosition(double opp) { player.setY(opp); }
    
    double getClientAccData() { return clientAccelerometerInput; }
    void setClientAccData(double newData) { clientAccelerometerInput = newData; }
    
    double getServerAccData() { return accelerometerInput; }
    void setServerAccData(double newData) { accelerometerInput = newData; }

    public Game_Graphics() throws IOException
    {
        weaponsGen = 200; 
        eog = 0;
        
        this.bulletPlayer = Image.createImage("/Main/bullet1.jpg");
        this.bulletOpp = Image.createImage("/Main/bullet.jpg");
        this.lose = createThumbnail(Image.createImage("/Main/lose.jpg"), getWidth() / 2);
        this.win = createThumbnail(Image.createImage("/Main/win.jpg"), getWidth() / 2);

        player = new paddle(0.96, 1.0/PowerPongMIDlet.fps);
        opponent = new paddle(0.04, 1.0/PowerPongMIDlet.fps);
        ball = new GameObjects(0, 0.2, 0.2, 0.55/PowerPongMIDlet.fps, 0.2/PowerPongMIDlet.fps, 0.02);      

        objects = new Vector (10, 1);

        this.button_man = new Button_Manager(this.getWidth(), this.getHeight(), 1);
        Main.PowerPongMIDlet.SetWidth(this.getWidth());
        Main.PowerPongMIDlet.SetHeight(this.getHeight());
        // add player buttons:
        String[] names = {"/Main/img1.jpg"};
        String[] names2 = {"/Main/img2.jpg"};
        
        button_man.addButtons(names, names2, 0.5, 0.75, false, (int) (Main.PowerPongMIDlet.GetWidth() / 6));// , UP, GAME_B);
        // set button actions:
        this.button_man.setButton(1, Main.Button_Manager.fire_weapon);
//        this.button_man.setButton(2, inc_opp_score);
//        this.button_man.setButton(3, fire_weapon);

//        this.setTitle("Accelerometer Test");

        this.addCommand(getCancelCommand());
        this.setCommandListener(this);
    }

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: getCancelCommand ">
    /**
     * Returns an initialized instance of cancelCommand component.
     * @return the initialized component instance
     */
    private Command getCancelCommand() {
       if (cancelCommand == null) {
            // write pre-init user code here
          cancelCommand = new Command("Cancel", Command.CANCEL, 0);
            // write post-init user code here
       }
       return cancelCommand;
    }
    //</editor-fold>

   //Implementation of how AI decides opponent velocity
   //

    public void AI()
    {
        if(opponent.getColour() != 0) //if invisible make AI dumb
            opponent.setVelocityMax(-1*Math.abs(opponent.getVelocityMax()));
        
       if (ball.getVelocityX() > 0 || ball.getX() > 0.54) //AI can ignore ball
       {
           if(objects.isEmpty()) //no power-ups
               opponent.setVelocityY((0.5 - opponent.getY() - opponent.getSize()/2)*2*opponent.getVelocityMax()); //move to middle
           
           else
           {
               GameObjects temp = (GameObjects) objects.elementAt(0); //respond to first power-up in vector
               if(temp.getVelocityX() < 0 && temp.getType() < 5) //get out of the way
               {
                    double v = (opponent.getY() + opponent.getSize()/2 - temp.getY());
                    if(v < 0)
                        v = -0.175 - v;
                    else
                        v = 0.175 - v;
                    opponent.setVelocityY(v*opponent.getVelocityMax());
               }
               else if (temp.getVelocityX() < 0 && temp.getType() >= 5) //go get it
               {
                    if (opponent.getY() + opponent.getSize()/2 > temp.getY())
                        opponent.setVelocityY(-0.62*Math.sqrt(opponent.getY() + opponent.getSize()/2 - temp.getY())*opponent.getVelocityMax());
                    else if(opponent.getY() + opponent.getSize()/2 < temp.getY())
                        opponent.setVelocityY(0.62*Math.sqrt(- opponent.getY() - opponent.getSize()/2 + temp.getY())*opponent.getVelocityMax());
               }
               
               else
                   opponent.setVelocityY((0.5 - opponent.getY() - opponent.getSize()/2)*2*opponent.getVelocityMax());
           }
           
           if(opponent.getAmmo() != 0 && Math.abs(0.7 - ball.getX()) < 0.1 && Math.abs(opponent.getY() - player.getY()) < 0.1) //can shoot a buller
           {
               doShoot--;
               if(doShoot == 0)
               {
                   forcedGenerate(1, true, opponent.getY() + opponent.getSize()/2); //type is bullet, isLeft is true, y-pos
                   opponent.decAmmo();
                   doShoot = 7;
               }
           }
       }
               
       else if (opponent.getY() + opponent.getSize()*0.35 > (ball.getY() + ball.getSize()/2))       //move up
           opponent.setVelocityY(-0.72*Math.sqrt(opponent.getY() + opponent.getSize() - ball.getY())*opponent.getVelocityMax());

       else if(opponent.getY() + opponent.getSize()*0.65 < (ball.getY() + ball.getSize()/2))        //move down
           opponent.setVelocityY(0.72*Math.sqrt(-opponent.getY() + (ball.getY() + ball.getSize()))*opponent.getVelocityMax());

       else     //don't move
          opponent.setVelocityY(0);
       
       if(opponent.getVelocityMax() < 0 && ((ball.getVelocityX() < 0 && ball.getX() < 0.37) || ball.getVelocityX() > 0))
           opponent.setVelocityY(-1*opponent.getVelocityY());
   }


   //Physics implemented
   //Called periodically to increment position.
   //

   public void move()
   {
       //Paddle-ball collisions
       //Implemented with weapons hit function move() in GameObjects.java
       //
       if(ball.move(15.0/getHeight())) //checks for collisions on paddles
       {
           if(ball.getY() < 0 || ball.getY() + ball.getSize() > 1) //hit the wall on the top or bottom
                ball.setVelocityY(-1*ball.getVelocityY());
           
           
           if(ball.getX() + ball.getSize() > (1 - 15.0/getWidth()) && ball.getVelocityX() > 0){
           
               if(player.isHit(ball.getY(), ball.getSize(), 0)) //hit player
                   ball.setVelocityX(-1*ball.getVelocityX());
                
               
               else if(ball.getX() + ball.getSize() > (1 - 14.0/getHeight()))
               {
                   player.decScore();
                   explode = 1;
                   expInc = 1;
                   explY = (int)(ball.getY()*getHeight())/*(int)Math.ceil(ball.getY()*getHeight()) - (int)Math.ceil(0.3*getWidth())/2*/;
                   explX = (int)(ball.getX()*getWidth())/*(int)Math.ceil(ball.getX()*getWidth()) - (int)Math.ceil(0.3*getWidth())/2*/;
                   ball.setVelocityX(-1*ball.getVelocityX());
               }
           }
           if((ball.getX() < 15.0/getWidth() && ball.getVelocityX() < 0))
           {
               if (opponent.isHit(ball.getY(),ball.getSize(), 0)) //hit opponent
                   ball.setVelocityX(-1*ball.getVelocityX());
           
               
               else if(ball.getX() < 15.0/getHeight())
               {
                   opponent.decScore();
                   explode = 1;
                   expInc = 1;
                   explY = (int)(ball.getY()*getHeight())/*(int)Math.ceil(ball.getY()*getHeight()) - (int)Math.ceil(0.3*getWidth())/2*/;
                   explX = (int)(ball.getX()*getWidth())/*(int)Math.ceil(ball.getX()*getWidth()) - (int)Math.ceil(0.3*getWidth())/2*/;
               
                   ball.setVelocityX(-1*ball.getVelocityX());
               }
           }
                   
           
           
           
       }
       //<editor-fold defaultstate="collapsed" desc="incremental movement of paddles and weapons">
       player.setVelocityY ((-1 + 2*accelerometerInput)*player.getVelocityMax());
       //the above code converts the accelerometerInput into a velocity
       if(Main.PowerPongMIDlet.getMode() != Main.PowerPongMIDlet.SINGLE)
            opponent.setVelocityY ((-1 + 2*clientAccelerometerInput)*opponent.getVelocityMax());

       if(opponent.move()) //true when off the playing field (need to bring one step back
       {
           opponent.setVelocityY(-1*opponent.getVelocityY());
           opponent.move();
       }

       if(player.move()) //true when off the playing field (need to bring one step back
       {
           player.setVelocityY(-1*player.getVelocityY());
           player.move();
       }

       for(int i = 0; i < objects.size(); i++)
       {
           GameObjects temp = (GameObjects) objects.elementAt(i);
           if(temp.move(14.0/getHeight()))  // true when object could hit paddles
           {
               boolean remove = false;

               if(temp.getX() > 0.5 && player.isHit(temp.getY(), temp.getSize(), temp.getType())) //hit player
                   remove = true;

               if (temp.getX() < 0.5 && opponent.isHit(temp.getY(),temp.getSize(), temp.getType())) //hit opponent
                   remove = true;

               if (temp.getX() + temp.getSize() > 1 || temp.getX() < 0) //out of field
                    remove = true;

               if(remove == true){
                   objects.removeElementAt(i);
                   i--;
               }
//               else
//                   Main.PowerPongMIDlet.bt.SendString("W" + temp.getType() + (int)(temp.getX()*1000) + "." + (int)(temp.getY()*1000));
           }
       }
       //</editor-fold>
       
       if(reloadTime != 0)
           reloadTime--;
       
       
   }

   //The following function generates
   //weapons and power-ups at periodic intervals
   //
   
   public void generate()
   {
       if(weaponsGen-- == 0)
       {
           Random rnd = new Random(System.currentTimeMillis());
           GameObjects temp = new GameObjects(0, 0.3, 0.5, 0.25/PowerPongMIDlet.fps, 0, 0.02);
           temp.setY(0.1 + 0.8*rnd.nextDouble());
           if(rnd.nextDouble() > 0.5)
           {
               temp.setVelocityX(-0.25/PowerPongMIDlet.fps);
               temp.setX(0.7);
           }
           switch((int)(15*rnd.nextDouble()))
           {
               case 7:
               case 8:
                   temp.setType(2);
                   temp.setColour(0x888888);
                   break;
                 
               case 2:
                   temp.setType(3);
                   temp.setColour(0x880088);
                   break;
                   
               case 3:
                   temp.setType(4);
                   temp.setColour(0xFF3344);
                   break;
                   
               case 4:
               case 5:
                   temp.setType(5);
                   temp.setColour(0x3399FF);
                   break;
                   
               case 6:
                   temp.setType(6);
                   temp.setColour(0x008844);
                   break;
               
               case 0:
               case 1:
               case 9:
               case 10:
               case 11:
               case 12:
               case 13:
               case 14:
                   temp.setType(7);
                   temp.setColour(0xCC6600);
                   break;

               default: 
                   break;
           }
           if(Main.PowerPongMIDlet.getMode() != Main.PowerPongMIDlet.SINGLE)
           {
               String send = "W " + Integer.toString(temp.getType());
               if(temp.getVelocityX() < 0)
                   send = send.concat("1");
               else
                   send = send.concat("0");
               send = send.concat(Double.toString(temp.getY()));
               Main.PowerPongMIDlet.bt.SendString(send);
           }
           objects.addElement(temp);
           weaponsGen = 80 + rnd.nextInt(30);
       }
   }
   
   public void forcedGenerate(int type, boolean isLeft, double y)
   {
       GameObjects temp = new GameObjects(type, 0.3, y, 0.25/PowerPongMIDlet.fps, 0, 0.02);
       
       
       switch(type)
            {
                case 1:
                    temp.setX(0.062); //bullets fired from paddle
                    temp.setVelocityX(5*temp.getVelocityX()); //bullets are faster
                    break;
                case 2:
                    temp.setColour(0x888888);
                    break;
                case 3:
                    temp.setColour(0x880088);
                    break;
                case 4:
                    temp.setColour(0xFF3344);
                    break;
                case 5:
                    temp.setColour(0x3399FF);
                    break;
                case 6:
                    temp.setColour(0x008844);
                    break;
                case 7:
                    temp.setColour(0xCC6600);
                    break;
            }
       
       if(!isLeft) //invert x position and speed
       {
           temp.setX(1 - temp.getX());
           temp.setVelocityX(-temp.getVelocityX());
       }
       objects.addElement(temp);
   }

   


   Font scoresFont = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE); 
   // paint debugging game
   protected void paint(Graphics g)
   {
        g.setColor(0xFFFFFF); //white
        g.fillRect(0, 0, this.getWidth(), this.getHeight()); //back drop
        
        if(eog == 0){
            g.setColor(0);
            try
            {
                button_man.DrawButtons(g);
            }
            catch(Exception e)
            {
                g.drawString(e.getMessage(), 0, 0, Graphics.TOP | Graphics.LEFT);
            }

            g.setColor(0xCCCCCC);
            g.fillRect(0, 0, 15, getHeight());
            g.fillRect(getWidth() - 15, 0, 15, getHeight());
            
            if(explode == 1){
                if(expInc == 1){
                    expSize += 0.05;
                    if(expSize >= 0.1)
                        expInc = 0;
                }
                if(expInc == 0){
                    expSize -= 0.03;
                    if(expSize <= 0.00)
                        explode = 0;
                }
                g.setColor(0xff8c00);
                int t = (int)Math.ceil(expSize*getWidth());
                if(explX < 0.5){

                    g.fillArc(explX-t, explY-t, 2*t, 
                        2*t, 0, 360);
                }else{
                    g.fillArc(explX-t, explY-t, 2*t, 
                        2*t, 0, 360);
                }
            }

            g.setColor(0xFF6666); //light pink
            g.setFont(scoresFont);
            g.drawString(opponent.getScore() + "   HP   " + player.getScore(),
                    getWidth()/2, getHeight()/3, Graphics.HCENTER | Graphics.BOTTOM); //Draw the scores on the screen

            g.setColor(player.getColour());
            g.fillRect(getWidth() - 15, (int)(getHeight()*player.getY()), 10, (int)(getHeight()*player.getSize())); //own paddle

            g.setColor(opponent.getColour());
            g.fillRect(5, (int)(opponent.getY()*getHeight()), 10, (int)(opponent.getSize()*getHeight()));           //opponent paddle

            g.setColor(0xFF9944); //Write ammo on screen
            g.drawString(opponent.getAmmo() + "   Ammo   " + player.getAmmo(),
                    getWidth()/2, getHeight()/5, Graphics.HCENTER | Graphics.BOTTOM);

            g.setColor(0);
            g.fillArc((int)Math.ceil(ball.getX()*getWidth()), (int)Math.ceil(ball.getY()*getHeight()), 
                    (int)Math.ceil(ball.getSize()*getWidth()), (int)Math.ceil(ball.getSize()*getWidth()), 0, 360); //ball


            for(int i = 0; i < objects.size(); i++)
            {
                GameObjects temp = (GameObjects)objects.elementAt(i);
                if(temp.type == 1){
                    if(temp.getVelocityX() < 0){
                        if(bulletPlayer != null){
                            g.drawImage(bulletPlayer, (int)Math.ceil(getWidth()*temp.getX()), 
                                    (int)Math.ceil(getHeight()*temp.getY()), Graphics.HCENTER | Graphics.VCENTER);
                        }else {
                            g.setColor(0x000000);
                            g.drawString("No image available", (int)Math.ceil(getWidth()*temp.getX()), 
                                    (int)Math.ceil(getHeight()*temp.getY()),  Graphics.HCENTER | Graphics.BASELINE);
                        }
                    }else{
                        if(bulletOpp != null){
                            g.drawImage(bulletOpp, (int)Math.ceil(getWidth()*temp.getX()), 
                                    (int)Math.ceil(getHeight()*temp.getY()), Graphics.HCENTER | Graphics.VCENTER);
                        }else {
                            g.setColor(0x000000);
                            g.drawString("No image available", (int)Math.ceil(getWidth()*temp.getX()), 
                                    (int)Math.ceil(getHeight()*temp.getY()),  Graphics.HCENTER | Graphics.BASELINE);
                        }
                    }
                }else{
                    g.setColor(temp.getColour());
                    g.fillArc((int)Math.ceil(getWidth()*temp.getX()), (int)Math.ceil(getHeight()*temp.getY()),
                        (int)Math.ceil(temp.getSize()*getWidth()), (int)Math.ceil(temp.getSize()*getWidth()), 0, 360);
                } 
            }
        }else if (eog == 1){
           try{
               if(player.getScore() == 0){
//                    this.lose = createThumbnail(lose, (int)Math.ceil(getWidth()*w));
                    w += 0.005;
                    if(lose != null){
                        g.drawImage(lose, (int)Math.ceil(getWidth()/2), 
                                (int)Math.ceil(getHeight())/2, Graphics.HCENTER | Graphics.VCENTER);
                    }else {
                        g.setColor(0x000000);
                        g.drawString("No image available", (int)Math.ceil(getWidth())/2, 
                                (int)Math.ceil(getHeight())/2,  Graphics.HCENTER | Graphics.BASELINE);
                    }
               }else{
//                   this.win = createThumbnail(win, (int)Math.ceil(getWidth()*w));
                    w += 0.005;
                    if(win != null){
                        g.drawImage(win, (int)Math.ceil(getWidth()/2), 
                                (int)Math.ceil(getHeight())/2, Graphics.HCENTER | Graphics.VCENTER);
                    }else {
                        g.setColor(0x000000);
                        g.drawString("No image available", (int)Math.ceil(getWidth())/2, 
                                (int)Math.ceil(getHeight())/2,  Graphics.HCENTER | Graphics.BASELINE);
                    }
               }
                
           }catch(Exception e){
                e.printStackTrace();
           }

           time++;
            if(time == 35){
                eog = 0;
                time = 0;
                w = 0.4;
                Main.PowerPongMIDlet.gfx.resetGame();
                if(Main.PowerPongMIDlet.getMode() == Main.PowerPongMIDlet.SERVER)
                    Main.PowerPongMIDlet.bt.SendString("restart");
            }
        }
        if(opponent.getScore() == 0 || player.getScore() == 0)
            eog = 1;
   }
  
   //<editor-fold defaultstate="collapsed" desc="Relay touch screen presses">
   int pointerX, pointerY;
   /**
    * Called when the pointer is pressed.
    */
   protected void pointerPressed(int x, int y) {
       button_man.ButtonPress(x,y);
   }
   
   /**
    * Called when the pointer is released.
    */
   protected void pointerReleased(int x, int y)
   {
       button_man.ButtonRelease(x,y);
   }
   //</editor-fold>

   // command button pressed
   public void commandAction(Command c, Displayable d) {
        if(c == cancelCommand)
        {
            Main.PowerPongMIDlet.stopGameThread();
            resetGame();
            Main.PowerPongMIDlet.showDisplayable(Main.PowerPongMIDlet.menu);
            Main.PowerPongMIDlet.bt.SendString("stop");
//            Main.PowerPongMIDlet.revertDisplayable();
        }
   }
   
   // Everything that happens between matches MUST be in this function. 
   public void resetGame()
   {
       Random rnd = new Random(System.currentTimeMillis());
       if(Main.PowerPongMIDlet.getMode() == Main.PowerPongMIDlet.SERVER)
       {
           ball.setVelocityX(Math.abs(ball.getVelocityX()));
           ball.setX(0.2);
       }
       else
       {
           ball.setVelocityX(-1*Math.abs(ball.getVelocityX()));
           ball.setX(0.8);
       }
       
       if(Main.PowerPongMIDlet.getMode() == Main.PowerPongMIDlet.SERVER)
       {
           ball.setY(0.3 + 0.4*rnd.nextDouble());
           if(Main.PowerPongMIDlet.bt.IsConnected()) // not connected if first game
               Main.PowerPongMIDlet.bt.SendString("BY" + Main.PowerPongMIDlet.gfx.getBall().getY());  // synchronize ball position with client on all but first game

//           ball.setVelocityY((-0.2 + 0.4*rnd.nextDouble())/PowerPongMIDlet.fps);
//           Main.PowerPongMIDlet.bt.SendString("VY" + ball.getVelocityY()); 
       }
       else if(Main.PowerPongMIDlet.getMode() == Main.PowerPongMIDlet.SINGLE)
       {
           ball.setY(0.3 + 0.4*rnd.nextDouble());
//           ball.setVelocityY((-0.2 + 0.4*rnd.nextDouble())/PowerPongMIDlet.fps);
       }

    
//       ball.setY(0.3);
       ball.setVelocityY((-0.2)/PowerPongMIDlet.fps);
       explode = 0; //set to '1' if the ball hits the back of one of the players sides
       expSize = 0.00; //size of explosion when ball hits the left and right sides of the screen
       expInc = 0; //indicates whether explosion is increasing or decreasing
       explY = 0; //the Y coordinate where the ball hit the side
       explX = 0; //the side of the screen where the ball hit

       objects.removeAllElements();
       weaponsGen = 200;
    
       
       eog = 0;
       player.ammo = 0;
       opponent.ammo = 0;
       player.setY(0.5);
       opponent.setY(0.5);
       player.setSize(0.22);
       opponent.setSize(0.22);
       player.setVelocityY(0);
       opponent.setVelocityY(0);
       player.setVelocityMax(1.0/PowerPongMIDlet.fps);
       opponent.setVelocityMax(1.0/PowerPongMIDlet.fps);
       Main.PowerPongMIDlet.gfx.accelerometerInput = 0.5;
       Main.PowerPongMIDlet.gfx.clientAccelerometerInput = 0.5;
       player.setColour(0);
       opponent.setColour(0);
       player.setScore(INIT_SCORE);
       opponent.setScore(INIT_SCORE);
   }
   
   //resize the picture to zoom in or out the picture
     private Image createThumbnail(Image image,int width)
    {
       int sourceWidth = image.getWidth();
       int sourceHeight = image.getHeight();
       int thumbWidth = width;
       int thumbHeight = -1;

       if (thumbHeight == -1)
          thumbHeight = thumbWidth * sourceHeight / sourceWidth;

       Image thumb = Image.createImage(thumbWidth, thumbHeight);
       Graphics g = thumb.getGraphics();

       for (int y = 0; y < thumbHeight; y++)
       {
          for (int x = 0; x < thumbWidth; x++)
          {
            g.setClip(x, y, 1, 1);
            int dx = x * sourceWidth / thumbWidth;
            int dy = y * sourceHeight / thumbHeight;
            g.drawImage(image, x - dx, y - dy, Graphics.LEFT | Graphics.TOP);
          }
       }

       Image immutableThumb = Image.createImage(thumb);

       return immutableThumb;
    }
  
}
  
   

